﻿using Micro.Modules.Abstractions;
using Micro.Utils;
using Micro.Utils.Constants;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Text;
using System.Threading.Tasks;

namespace Micro.Modules
{

    /// <summary>
    /// 模块程序集加载上下文
    /// https://docs.microsoft.com/zh-cn/dotnet/core/dependency-loading/understanding-assemblyloadcontext
    /// https://docs.microsoft.com/zh-cn/dotnet/core/tutorials/creating-app-with-plugin-support
    /// </summary>
    public class ModuleAssemblyLoadContext : AssemblyLoadContext, IModuleAssemblyLoadContext
    {
        /// <summary>
        /// 依懒项
        /// </summary>
        private AssemblyDependencyResolver _resolver;
        private string DllPath { get; set; }

        public ModuleAssemblyLoadContext(string dllPath, string moduelCode, bool isCollectible = false) : base(name: $"{moduelCode}", isCollectible: isCollectible)
        {
            DllPath = dllPath;

            var webPath = Path.Combine(dllPath, $"Module.{moduelCode}.Web.dll");
            var apiPath = Path.Combine(dllPath, $"Module.{moduelCode}.Api.dll");

            string mainPath = File.Exists(webPath) ? webPath : apiPath;
            if (File.Exists(mainPath))
                _resolver = new AssemblyDependencyResolver(mainPath);

            this.Resolving += _AssemblyLoadContext_Resolving;
            this.Unloading += _AssemblyLoadContext_Unloading;
            LoadDLL();
        }

        protected override Assembly Load(AssemblyName assemblyName)
        {
            //if (_resolver != null && !ModuleConst.ExcludeDll.Any(m => m.Equals(assemblyName.Name, StringComparison.CurrentCultureIgnoreCase)))
            if (!ModuleConstant.ExcludeDll.Any(m => m.Equals(assemblyName.Name, StringComparison.CurrentCultureIgnoreCase)))
            {
                //string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
                string assemblyPath = Path.Combine(DllPath, assemblyName.Name + ".dll");

                return this.Add(assemblyPath);
            }

            return null;
        }

        protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
        {
            string libraryPath = Path.Combine(DllPath, unmanagedDllName + ".dll");
            //string libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
            if (libraryPath != null && !ModuleConstant.ExcludeDll.Any(m => m.Equals(unmanagedDllName, StringComparison.CurrentCultureIgnoreCase)))
            {
                return LoadUnmanagedDllFromPath(libraryPath);
            }

            return IntPtr.Zero;
        }

        /// <summary>
        /// 加载依赖
        /// </summary>
        /// <param name="arg1"></param>
        /// <param name="arg2"></param>
        /// <returns></returns>
        public Assembly _AssemblyLoadContext_Resolving(AssemblyLoadContext context, AssemblyName assemblyName)
        {
            //Console.WriteLine($"加载{arg2.Name}");
            //var path = _resolver.ResolveAssemblyToPath(assemblyName);
            string path = Path.Combine(DllPath, assemblyName.Name + ".dll");
            return this.Add(path);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        public void _AssemblyLoadContext_Unloading(AssemblyLoadContext obj)
        {
            //throw new NotImplementedException();
        }


        public Assembly Add(string path)
        {
            if (!string.IsNullOrWhiteSpace(path) && File.Exists(path))
            {
                //this.LoadFromAssemblyPath(path);
                using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
                {
                    return this.LoadFromStream(fs);
                }
            }
            return null;
        }


        /// <summary>
        /// 加载全部dll
        /// </summary>
        /// <param name="context"></param>
        private void LoadDLL()
        {
            if (string.IsNullOrEmpty(DllPath) || !Directory.Exists(DllPath))
                return;
            var files = new DirectoryInfo(DllPath).GetFiles().Where(m => m.Name.EndsWith(".dll")).ToList();
            if (!files.Any())
                return;
            foreach (var file in files)
            {
                //忽略dll
                if (!ModuleConstant.ExcludeDll.Any(m => m.Equals(Path.GetFileNameWithoutExtension(file.Name), StringComparison.CurrentCultureIgnoreCase)))
                {
                    var assembly = this.Add(file.FullName);

                }
            }

        }

        public Assembly Get(string assemblyName)
        {
            if (string.IsNullOrWhiteSpace(assemblyName))
                return null;
            var assembly = this.Assemblies.FirstOrDefault(m => assemblyName.Equals(m.GetName().Name ?? "", StringComparison.OrdinalIgnoreCase))
                ?? AssemblyHelper.LoadByNameEndString(assemblyName);
            return assembly;
        }

        public IEnumerable<Assembly> GetAssemblies()
        {
            return this.Assemblies;
        }
    }
}
